class mvvm {
    constructor(_obj) {
        this.$data = _obj.data;
        // 数据劫持操作，监视数据中属性的变化
        Observer(this.$data);
        Compile(_obj.el, this);
    }
}

// 数据劫持 - 监听实例中数据的改变
function Observer(_data) {
    // 如果没有数据或者数据不是对象类型就退出函数
    if (!_data || typeof _data !== 'object') return;
    // 每次数据劫持时都要创建Dependency实例以便于区分每个对象对应的依赖
    const dep = new Dependency();
    // 遍历每一个数据的所有值
    Object.keys(_data).forEach(key => {
        let val = _data[key];
        // 递归调用，实现子数据的劫持
        Observer(val);

        // 数据劫持，实现get，set方法
        Object.defineProperty(_data, key, {
            enumerable: false,
            configurable: true,
            get() {
                if (Dependency.temp) {
                    dep.addSubscribers(Dependency.temp);
                }
                console.log(`访问属性:${key} -> 值:${val}`);
                return val;
            },
            // 更新视图
            set(newVal) {

                console.log(`属性${key}的值${val}修改为 -> ${newVal}`);
                val = newVal;//-------------------
                Observer(newVal);
                dep.notify();
            }
        });
    });
}

//html模板解析 -替换DOM内容,将数据解析至页面
function Compile(element, vm) {
    //获取对象vm中的元素
    vm.$el = document.querySelector(element);
    //文档碎片存储DOM元素 减少DOM更新
    const fragment = document.createDocumentFragment();

    let child;
    // 将子节点全部存放在文件碎片中
    while ((child = vm.$el.firstChild)) {
        fragment.appendChild(child);
    }
    fragment_compile(fragment);

    // 替换文档碎片中节点内容
    function fragment_compile(node) {
        // 正则表达式匹配{{ data }}
        const regex = /\{\{\s*(\S+)\s*\}\}/;

        // 如果当前节点是文本节点
        if (node.nodeType === 3) {
            // 将能匹配正则表达式的值记录方便之后使用
            const _temp = node.nodeValue;
            const result = regex.exec(node.nodeValue);
            if (result) {

                // split字符串分割为数组
                const arr = result[1].split('.');
                // reduce链式获取属性的子属性的值
                const value = arr.reduce(
                    (total, current) => total[current], vm.$data
                );
                // replace(匹配的值，用于替换的值)将当前节点值进行替换     然后赋值把原来的值覆盖
                node.nodeValue = _temp.replace(regex, value);

                new Watcher(vm, result[1], newVal => {
                    node.nodeValue = _temp.replace(regex, newVal);
                });
            }
        }
        // 如果当前节点是元素节点 并且标签名称为input
        if (node.nodeType === 1) {
            // 获取节点的属性并转化成数组
            const attr = Array.from(node.attributes);
            attr.forEach(i => {
                if (i.nodeName === 'v-model' && node.nodeName === 'INPUT') {
                    const value = i.nodeValue.split('.').reduce(
                        (total, current) => total[current], vm.$data
                    );
                    node.value = value;
                    // 添加订阅者
                    new Watcher(vm, i.nodeValue, newValue => {
                        node.value = newValue;
                    });
                    // 监听器实现视图改变数据
                    node.addEventListener('input', e => {
                        const temp1 = i.nodeValue.split('.');
                        const temp2 = temp1.slice(0, temp1.length - 1);
                        const arr = temp2.reduce(
                            (total, current) => total[current], vm.$data
                        );
                        // vm$data.more['message'] = e.target.value
                        arr[temp1[temp1.length - 1]] = e.target.value;
                    });
                }
                else if (i.nodeName === 'v-bind:value' && node.nodeName === 'INPUT') {
                    const value = i.nodeValue.split('.').reduce(
                        (total, current) => total[current], vm.$data
                    );
                    node.value = value;


                    new Watcher(vm, i.nodeValue, newValue => {
                        node.value = newValue;
                    });
                }
            });
        }

        // 递归对子节点进行替换操作
        node.childNodes.forEach(child => fragment_compile(child));
    }
    // 将文档碎片运用值vm的属性中
    vm.$el.appendChild(fragment);
}

//依赖-收集
class Dependency {
    constructor() {
        this.subscribers = [];
    }
    //添加订阅者
    addSubscribers(sub) {
        this.subscribers.push(sub);
    }
    // 通知订阅者更新
    notify() {
        this.subscribers.forEach(sub => sub.update());
    }
}

// 订阅者
class Watcher {
    constructor(vm, key, callback) {
        this.vm = vm;
        this.key = key;
        this.callback = callback;

        // temp：临时属性，当使用get将订阅者存储至Dependency实例subscribers中
        Dependency.temp = this;
        key.split('.').reduce((total, current) => total[current], vm.$data);
        Dependency.temp = null;
    }
    //更新数据
    update() {
        const value = this.key.split('.').reduce(
            (total, current) => total[current], this.vm.$data
        );
        this.callback(value);
    }
}